#ifndef SC_SETUP_H_INCLUDED
#define SC_SETUP_H_INCLUDED

#include "sc/config.h"

#include <libxml/xmlwriter.h>

#include <list>
#include <vector>
#include <set>

namespace sc {

namespace setup {
/**
* Class Attribute
*/
class Attribute
{
public:
    Attribute(const std::string& val, const cfg::Attribute& rcConf)
        : m_sValue(val), m_rcConf(rcConf) {}

    std::string               m_sValue;
    const cfg::Attribute&  m_rcConf;
};

/**
* Class Element
*/
class Element;
typedef std::list<Element>    TElements;
typedef std::list<Attribute>  TAttributes;

class Element
{
public:
    Element(const std::string& name = "", const std::string& content = "");
    Element(const std::string& name, const std::string& content, cfg::TElements::const_iterator iter);
    ~Element();

protected:
    /// Setup attributes
    std::string m_sName;
    std::string m_sContent;

    TAttributes   m_attributes;
    TElements     m_children;

    cfg::TElements::const_iterator  m_config;

public:
    /// Setup Element public methods
    int AddChild(const Element& rcElement, TElements::iterator& rIterChild);
    int AddChild(const Element& rcElement);
    int AddUniqueChildren(void);

    bool IsValid(void) const;
    bool IsChildAvailable(const std::string& sChildName) const;
    void GetAvailableChildren(std::set<std::string>& rListChild) const;

    const std::string GetName(void) const { return m_sName; }
    const std::string GetContent(void) const { return m_sContent; }
    const TAttributes& GetAttributes(void) const { return m_attributes; }
    const TElements& GetChildren(void) const { return m_children; }

    TElements::iterator ChildrenBegin(void) { return m_children.begin(); }
    TElements::iterator ChildrenEnd(void) { return m_children.end(); }

    TAttributes::iterator AttributesBegin(void) { return m_attributes.begin(); }
    TAttributes::iterator AttributesEnd(void) { return m_attributes.end(); }

    cfg::TElements::const_iterator GetConfig(void) const { return m_config; }

    int Delete(const Element* pcElement);

    int Load(xmlNodePtr xmlNode);
    int Save(xmlTextWriterPtr xmlWriter) const;

    cfg::Element::Type GetConfigType(void) const { return m_config->second.GetType(); }

protected:
    /**
    * @class PredFindConfigChildName
    *
    * Predicate to find a child configuration by name
    */
    class PredFindConfigChildName
    {
    public:
        PredFindConfigChildName(const std::string& name)
            : m_name(name) {}

    protected:
        std::string m_name;

    public:
        bool operator() (const cfg::TChildren::value_type& val)
        { return val->second.GetName() == m_name; }
    };

    /**
    * @class PredFindChildName
    *
    * Predicate to find if a child exist
    */
    class PredFindChildName
    {
    public:
        PredFindChildName(const std::string& name)
            : m_name(name) {}

    protected:
        std::string m_name;

    public:
        bool operator() (const TElements::value_type& val)
        { return val.m_sName == m_name; }
    };
};

/**
* @class PredFindAttributeByName
*/
class PredFindAttributeByName
{
private:
    std::string m_sName;

public:
    PredFindAttributeByName(const std::string& sName)
    : m_sName(sName)
    { }

    bool operator() (const setup::Attribute& rcAttribute)
    {
        return m_sName == rcAttribute.m_rcConf.GetName();
    }
};

}; // namespace setup

class Setup
{
public:
	Setup();
	~Setup();

protected:
	Config        m_config;
	setup::Element* m_pRoot;

public:
	const Config& GetConfig(void) const { return m_config; }
	const setup::Element* Get(void) const { return m_pRoot; }
	setup::Element* Get(void) { return m_pRoot; }

	int LoadConfig(const std::string& sConfigFilename);
	int Load(const std::string& sFilename);
	int Save(const std::string& sFilename) const;

	int DeleteElement(const setup::Element* pcElement);

	void Clear(void);

protected:
    int AddUniqueChildren(setup::Element& rElement);
};

}; // namespace sc

#endif //  SC_SETUP_H_INCLUDED
